package com.gan.simple_init.service.impl;

import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gan.simple_init.common.constant.RedisConstant;
import com.gan.simple_init.common.constant.UserConstant;
import com.gan.simple_init.common.exception.BusinessException;
import com.gan.simple_init.common.base.ResponseErrorEnum;
import com.gan.simple_init.common.base.ResponseResult;
import com.gan.simple_init.common.base.RestResultGenerator;
import com.gan.simple_init.mapper.SysUserMapper;
import com.gan.simple_init.model.user.dto.UserLoginDTO;
import com.gan.simple_init.model.user.dto.UserUpdateDTO;
import com.gan.simple_init.model.user.entity.LoginUser;
import com.gan.simple_init.model.user.entity.SysUser;
import com.gan.simple_init.service.SysUserService;
import com.gan.simple_init.util.JwtTokenUtil;
import com.gan.simple_init.util.RedisUtil;
import com.gan.simple_init.util.UserCacheUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;

/**
 * @author gbw
 * @description 针对表【sys_user(用户)】的数据库操作Service实现
 * @createDate 2023-08-08 17:25:27
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser>
        implements SysUserService {

    @Resource
    private UserDetailsServiceImpl userDetailsService;

    @Resource
    private UserCacheUtil userCacheUtil;

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private JwtTokenUtil jwtTokenUtil;

    @Value("${jwt.refreshExpiration}")
    private Long refreshExpiration;


    @Override
    public UserDetails getLoginUser(String userName) {
        //根据上面得到的身份查询user
        LoginUser loginUser = new LoginUser();
        QueryWrapper<SysUser> qw = new QueryWrapper<>();
        qw.eq("userName", userName);

        SysUser user = this.getOne(qw);
        if (Objects.isNull(user)) {
            throw new BusinessException("用户名或密码错误!");
        }
        BeanUtils.copyProperties(user, loginUser);
        List<String> roles = sysUserMapper.selectRolesById(user.getId());
        loginUser.setRoles(roles);
        return loginUser;
    }

    @Override
    public ResponseResult<?> login(HttpServletRequest httpServletRequest, UserLoginDTO userLoginDTO) {
        String userName = userLoginDTO.getUserName();
        String password = userLoginDTO.getPassword();

        // 获得失败次数
        Object num = redisUtil.get(RedisConstant.RESTRICT_LOGIN_KEY + userName);
        Integer failNum = num == null ? 0 : (Integer) num;
        // 失败次数超过五次限制用户
        if (failNum.compareTo(RedisConstant.RESTRICT_LOGIN_NUM) >= 0) {
            redisUtil.set(RedisConstant.RESTRICT_KEY + userName, RedisConstant.RESTRICT_NUM, RedisConstant.RESTRICT_TIME);
        }
        if (redisUtil.get(RedisConstant.RESTRICT_KEY + userName) != null) {
            throw new BusinessException("登录过于频繁，请稍后再试!");
        }

        try {
            // 从数据库中查询详细信息
            LoginUser loginUser = (LoginUser) userDetailsService.loadUserByUsername(userName);
            if (Objects.isNull(loginUser)) {
                throw new BusinessException(ResponseErrorEnum.USER_NO_EXIST);
            }
            // 解密密码，与用户输入的密码进行比对
            AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, UserConstant.SALT.getBytes());
            String decodePass = aes.decryptStr(loginUser.getPassword());
            // 用户名/密码 错误
            if (!decodePass.equals(password)) {
                throw new BusinessException(ResponseErrorEnum.USER_PASS_WARN);
            } else {
                // 更新 Security 登录用户对象
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                // 根据用户信息生成 token ，并将用户信息保存到 redis 中
                String token = userCacheUtil.genTokenToCache(httpServletRequest, loginUser);
                // 登录成功，清除redis中的数据
                redisUtil.del(RedisConstant.RESTRICT_LOGIN_KEY + userName);
                return RestResultGenerator.success(token, "登录成功!");
            }
        } catch (BusinessException e) {
            // 登录失败次数加一
            redisUtil.set(RedisConstant.RESTRICT_LOGIN_KEY + userName, failNum + 1, RedisConstant.RESTRICT_TIME);
            throw new BusinessException(e.getMessage() + ",还能重试" + (RedisConstant.RESTRICT_LOGIN_NUM - failNum) + "次");
        }
    }

    @Override
    public ResponseResult<?> register(UserLoginDTO userLoginDTO) {
        // 从数据库查询是否有该用户
        SysUser userInfo = getUserInfoByUserName(userLoginDTO.getUserName());
        if (userLoginDTO == null) {
            return RestResultGenerator.error("您输入的信息不正确！");
        } else if (userInfo != null) {
            return RestResultGenerator.error("用户名已存在请重新输入！");
        } else {
            // 将用户信息插入到数据库
            SysUser newUser = new SysUser();
            newUser.setUserName(userLoginDTO.getUserName());
            // 使用aes加密并进行Base转码
            AES aes = new AES(Mode.ECB, Padding.PKCS5Padding, UserConstant.SALT.getBytes());
            newUser.setPassword(aes.encryptBase64(userLoginDTO.getPassword()));
            // 插入数据
            int flag = baseMapper.insert(newUser);
            // 新注册用户默认为普通用户
            sysUserMapper.insertNewUserRole(newUser.getId(), UserConstant.ROLE_COMMON_USER);
            if (flag > 0) {
                return RestResultGenerator.success("注册成功!");
            }
            return RestResultGenerator.error("注册失败!");
        }
    }

    /**
     * 根据用户名查询用户信息
     *
     * @param userName 用户名
     * @return SysAccount 用户对象信息
     */
    @Override
    public SysUser getUserInfoByUserName(String userName) {
        QueryWrapper<SysUser> qw = new QueryWrapper<>();
        qw.eq("userName", userName);
        return getOne(qw);
    }

    /**
     * 刷新 token 信息
     *
     * @param request
     * @return
     */
    @Override
    public ResponseResult<?> refreshToken(HttpServletRequest request) {
        // 获取 token
        String token = request.getHeader("token");
        if (StringUtils.isNotEmpty(token) && StringUtils.isNotEmpty(jwtTokenUtil.getUserNameFromToken(token))) {
            // 从会话中查询用户信息
            UserDetails userDetails = UserCacheUtil.getUserCache();
            boolean tokenFlag = jwtTokenUtil.getTokenFlag(token);
            if (jwtTokenUtil.validateToken(token, userDetails) && tokenFlag) {
                // 根据用户信息生成 token 并插入 redis
                if (Objects.isNull(userDetails)) {
                    throw new BusinessException("用户信息不存在!");
                }
                String newToken = jwtTokenUtil.generateToken(userDetails, UserCacheUtil.getUserCache().getId());
                redisUtil.set(RedisConstant.USER_LOGIN_KEY + newToken, "token", refreshExpiration);
                // 删除掉原来的 token
                jwtTokenUtil.removeRedisToken(request);
                return RestResultGenerator.success(newToken, "刷新成功!");
            }
        }
        return RestResultGenerator.error(ResponseErrorEnum.USER_UN_LOGIN);
    }

}




